package com.yunchao.test.data.repository

import android.content.Context
import android.os.Environment
import com.yunchao.test.data.datasource.MaintenanceInfoDao
import com.yunchao.test.data.datasource.model.toDomainModel
import com.yunchao.test.domain.model.MaintenanceInfoModel
import com.yunchao.test.domain.model.toEntityModel
import com.yunchao.test.domain.repository.MaintenanceInfoRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.apache.poi.hssf.usermodel.HSSFSheet
import org.apache.poi.hssf.usermodel.HSSFWorkbook
import org.apache.poi.openxml4j.opc.OPCPackage
import org.apache.poi.ss.usermodel.Cell
import org.apache.poi.ss.usermodel.CellType
import org.apache.poi.ss.usermodel.DataFormatter
import org.apache.poi.ss.usermodel.DateUtil
import org.apache.poi.ss.usermodel.Row
import org.apache.poi.xssf.eventusermodel.XSSFReader
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler
import org.apache.poi.xssf.model.SharedStrings
import org.apache.poi.xssf.model.StylesTable
import org.apache.poi.xssf.usermodel.XSSFComment
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.xml.sax.InputSource
import org.xml.sax.XMLReader
import org.xml.sax.helpers.XMLReaderFactory
import timber.log.Timber
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.math.BigDecimal
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

internal class MaintenanceInfoRepositoryImpl(
    private val context: Context,
    private val maintenanceInfoDao: MaintenanceInfoDao,
) : MaintenanceInfoRepository {

    // 定义常量，用于存储 Excel 文件的扩展名
    private companion object {
        private const val XLS_EXTENSION = ".xls"
        private const val XLSX_EXTENSION = ".xlsx"

        private const val BATCH_SIZE = 1000 // 每批处理的行数
        private const val NUM_OF_COROUTINES = 3 // 并行处理的协程数
    }

    override suspend fun getMaintenanceFileList(): Result<List<File>> =
        withContext(Dispatchers.IO) {
            try {
                // 获取文件目录
                context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)?.let { filesDir ->
                    // 检查目录是否存在且为目录
                    if (filesDir.exists() && filesDir.isDirectory) {
                        // 过滤出 Excel 文件
                        val excelFiles =
                            filesDir.listFiles()?.filter { it.isFile && isExcelFile(it.name) }
                                ?: emptyList()
                        Result.success(excelFiles)
                    } else {
                        // 目录不存在，返回失败结果
                        Result.failure(IllegalStateException("目录不存在"))
                    }
                } ?: Result.failure(IllegalStateException("目录不存在"))

            } catch (e: Exception) {
                // 捕获异常并返回失败结果
                Result.failure(e)
            }
        }

    override suspend fun addMaintenanceInfoList(fileName: String): Result<Boolean> =
        withContext(Dispatchers.IO) {
            try {
                // 获取文件
                val file =
                    File(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), fileName)
                Timber.i(file.absolutePath)
                // 检查文件是否存在
                if (file.exists() && file.canRead()) {
                    readExcelFile(file.absolutePath).apply {
                        maintenanceInfoDao.insertMaintenanceInfo(this.map { it.toEntityModel() })
                    }.let {
                        Timber.i("插入成功 ${it.size} 条数据")
                        Result.success(true)
                    }

                } else {
                    // 文件不存在，返回失败结果
                    Result.failure(IllegalStateException("File does not exist"))
                }
            } catch (e: Exception) {
                // 捕获异常并返回失败结果
                Result.failure(e)
            }
        }

    override suspend fun getMaintenanceInfoByLockNo(lockNo: String): Result<MaintenanceInfoModel?> =
        runCatching {
            // 从数据库获取数据并转换为领域模型
            maintenanceInfoDao.getMaintenanceInfoByLockNo(lockNo)?.toDomainModel()
        }
    override suspend fun writeDataToExcel(models: List<MaintenanceInfoModel>): Result<Boolean> =
        withContext(Dispatchers.IO) {
            // 定义表头
            val headers = listOf("锁号", "出厂日期", "销售地点", "质保日期", "锁TID")
            val data = listOf(headers) + models.map { model ->
                listOf(
                    model.lockNo,
                    model.manufactureDate,
                    model.salesLocation,
                    model.warrantyPeriod,
                    model.lockTid
                )
            }

            // 获取当前时间
            val dateFormat = SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault())
            val currentTime = dateFormat.format(Date())

            // 以当前时间命名文件
            val fileName = "$currentTime.xlsx"
            // 保存文件

            val file = File(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), fileName)

            try {
                XSSFWorkbook().use { workbook ->
                    // 创建工作表
                    val sheet = workbook.createSheet("Sheet1")

                    // 遍历数据并写入工作表
                    data.forEachIndexed { rowIndex, rowData ->
                        val row = sheet.createRow(rowIndex)
                        rowData.forEachIndexed { cellIndex, cellValue ->
                            val cell = row.createCell(cellIndex)
                            cell.setCellValue(cellValue)
                        }
                    }

                    FileOutputStream(file).use { fileOut ->
                        workbook.write(fileOut)
                    }
                }
                Result.success(true)
            } catch (e: IOException) {
                Result.failure(e)
            }
        }

    private fun isExcelFile(fileName: String): Boolean {
        // 检查文件名是否以 .xls 或 .xlsx 结尾
        return fileName.endsWith(XLS_EXTENSION, ignoreCase = true) || fileName.endsWith(
            XLSX_EXTENSION,
            ignoreCase = true
        )
    }


    private suspend fun readExcelFile(filePath: String): List<MaintenanceInfoModel> =
        withContext(Dispatchers.IO) {
            if (filePath.isBlank()) {
                Timber.e("ExcelReader 提供的文件路径为空")
                return@withContext emptyList()
            }
            val dataList = mutableListOf<MaintenanceInfoModel>()
            try {
                when {
                    filePath.endsWith(XLS_EXTENSION, ignoreCase = true) -> readHSSFFileInParallel(
                        filePath,
                        dataList
                    )

                    filePath.endsWith(XLSX_EXTENSION, ignoreCase = true) -> readXSSFFile(
                        filePath,
                        dataList
                    )

                    else -> Timber.e("ExcelReader 不支持的文件格式: $filePath")
                }
            } catch (e: Exception) {
                Timber.e(e, "ExcelReader 读取文件时发生错误: ${e.message}")
            }
            dataList
        }

    private suspend fun readHSSFFileInParallel(
        filePath: String,
        dataList: MutableList<MaintenanceInfoModel>
    ) = coroutineScope {
        FileInputStream(File(filePath)).use { fileInputStream ->
            Timber.d("ExcelReader xls 成功打开文件: $filePath")
            HSSFWorkbook(fileInputStream).use { workbook ->
                val sheet = workbook.getSheetAt(0)
                val totalRows = sheet.lastRowNum
                val rowsPerCoroutine = totalRows / NUM_OF_COROUTINES

                val jobs = (0 until NUM_OF_COROUTINES).map { i ->
                    val startRow = if (i == 0) 1 else i * rowsPerCoroutine + 1
                    val endRow =
                        if (i == NUM_OF_COROUTINES - 1) totalRows else (i + 1) * rowsPerCoroutine
                    launch { processRows(sheet, startRow, endRow, dataList) }
                }
                jobs.joinAll()
            }
        }
    }


    private fun processRows(
        sheet: HSSFSheet,
        startRow: Int,
        endRow: Int,
        dataList: MutableList<MaintenanceInfoModel>
    ) {
        for (rowIndex in startRow..endRow) {
            val row = sheet.getRow(rowIndex)
            if (row == null) {
                Timber.e("ExcelReader-xls 第 $rowIndex 行数据为空，跳过")
                continue
            }

            val rowData = getRowData(row)
            if (rowData.size == 5) {
                val (lockNo, manufactureDate, salesLocation, warrantyPeriod, lockTid) = rowData
                synchronized(dataList) {
                    dataList.add(
                        MaintenanceInfoModel(
                            lockNo = lockNo,
                            manufactureDate = manufactureDate,
                            salesLocation = salesLocation,
                            warrantyPeriod = warrantyPeriod,
                            lockTid = lockTid
                        )
                    )
                }
            } else {
                Timber.w("ExcelReader-xls 第 $rowIndex 行数据列数不足 5，跳过")
            }
        }
    }

    private fun readXSSFFile( filePath: String, dataList: MutableList<MaintenanceInfoModel> ) {
        try {
            OPCPackage.open(filePath).use { opcPackage ->
                Timber.d("ExcelReader-xlsx 成功打开文件: $filePath")
                val xssfReader = XSSFReader(opcPackage)
                val sharedStringsTable = xssfReader.sharedStringsTable
                val styles = xssfReader.stylesTable
                val dataFormatter = DataFormatter()

                val sheetsData = xssfReader.sheetsData
                while (sheetsData.hasNext()) {
                    val sheetStream = sheetsData.next()
                    sheetStream.use {
                        createXmlReader(
                            sharedStringsTable,
                            styles,
                            dataFormatter,
                            dataList
                        ).parse(InputSource(it))
                    }
                }
            }
        } catch (e: FileNotFoundException) {
            Timber.e(e, "ExcelReader-xlsx 文件未找到: $filePath")
        } catch (e: IOException) {
            Timber.e(e, "ExcelReader-xlsx 读取文件时发生 IO 错误: ${e.message}")
        } catch (e: Exception) {
            Timber.e(e, "ExcelReader-xlsx 读取文件时发生未知错误: ${e.message}")
        }
    }

    private fun createXmlReader(
        sharedStringsTable: SharedStrings,
        styles: StylesTable,
        dataFormatter: DataFormatter,
        dataList: MutableList<MaintenanceInfoModel>
    ): XMLReader {
        val xmlReader = XMLReaderFactory.createXMLReader()
        val batch = mutableListOf<MaintenanceInfoModel>()
        var rowIndex = 1
        var rowData = mutableListOf<String>()

        xmlReader.contentHandler = XSSFSheetXMLHandler(
            styles,
            sharedStringsTable,
            object : XSSFSheetXMLHandler.SheetContentsHandler {
                override fun startRow(rowNum: Int) {
                    rowIndex = rowNum
                    if (rowNum >= 1) {
                        rowData.clear()
                    }
                }

                override fun endRow(rowNum: Int) {
                    if (rowNum < 1) return
                    processRow(rowData, rowNum, batch)
                    if (batch.size >= BATCH_SIZE) {
                        dataList.addAll(batch)
                        batch.clear()
                    }
                }

                override fun cell(
                    cellReference: String?,
                    formattedValue: String?,
                    comment: XSSFComment?
                ) {
                    if (rowIndex < 1) return
                    rowData.add(formattedValue.orEmpty())
                }

                override fun endSheet() {
                    if (batch.isNotEmpty()) {
                        dataList.addAll(batch)
                        batch.clear()
                    }
                }
            },
            dataFormatter,
            false
        )
        return xmlReader
    }

    private fun processRow(
        rowData: MutableList<String>,
        rowIndex: Int,
        batch: MutableList<MaintenanceInfoModel>
    ) {
        if (rowData.size == 4) {
            val (lockNo, manufactureDate, salesLocation, warrantyPeriod) = rowData
            batch.add(
                MaintenanceInfoModel(
                    lockNo = lockNo,
                    manufactureDate = manufactureDate,
                    salesLocation = salesLocation,
                    warrantyPeriod = warrantyPeriod,
                    lockTid = ""
                )
            )
        } else if (rowData.size == 5) {
            val (lockNo, manufactureDate, salesLocation, warrantyPeriod, lockTid) = rowData
            batch.add(
                MaintenanceInfoModel(
                    lockNo = lockNo,
                    manufactureDate = manufactureDate,
                    salesLocation = salesLocation,
                    warrantyPeriod = warrantyPeriod,
                    lockTid = lockTid
                )
            )
        } else {
            Timber.w("ExcelReader-xlsx  第 $rowIndex 行数据列数不足，跳过 $rowData")
        }
    }

    private fun getRowData(row: Row): MutableList<String> {
        val rowData = mutableListOf<String>()
        for (cellIndex in 0 until 5) {
            val cell = row.getCell(cellIndex)
            rowData.add(getCellValue(cell))
        }
        return rowData
    }

    private fun getCellValue(cell: Cell?): String {
        return when (cell?.cellType) {
            CellType.STRING -> cell.stringCellValue
            CellType.NUMERIC -> {
                Timber.d("getCellValue ${cell.cellType} $cell")
                if (DateUtil.isCellDateFormatted(cell)) {
                    val date: Date = cell.dateCellValue
                    SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(date)
                } else {
                    BigDecimal(cell.numericCellValue.toString()).toPlainString()
                }
            }

            CellType.BOOLEAN -> cell.booleanCellValue.toString()
            else -> ""
        }
    }
}